﻿import imp
import tkinter as tk, tkinter.ttk as ttk,tkinter.messagebox, socket
import ui_data, ui_conf, channel,  mainframe, mainstate
import about
"""通讯规约列表"""

class MainList(tk.Frame):
    """通讯规约列表"""
    def __init__(self, parent:tk.PanedWindow, UI:ui_data.UI_data ) -> None:
        super().__init__(parent)
        self.UI:ui_data.UI_data = UI
        scrollbarX = tk.Scrollbar(self, orient="horizontal")
        scrollbarX.pack(fill="x", side="bottom", anchor="s")
        scrollbarY = tk.Scrollbar(self, orient="vertical" )
        scrollbarY.pack(fill="y",side="right", anchor="e")
        self.mainlist = ttk.Treeview(self,
                                    name="mainlist",
                                    selectmode = "extended",
                                    xscrollcommand=scrollbarX.set,
                                    yscrollcommand=scrollbarY.set,
                                    columns=["Channel", "Protocol", "Configure", ] )
        self.mainlist.column("#0", width=60, minwidth=30,anchor="w", stretch =False)
        self.mainlist.column("Channel", width=50, minwidth=30, anchor="w", stretch =False)
        self.mainlist.column("Protocol", width=50, minwidth=30, anchor="w", stretch =False)
        self.mainlist.column("Configure", width= 40, anchor="w")
        self.mainlist.heading("#0", text= "No")
        self.mainlist.heading("Channel", text= "通道")
        self.mainlist.heading("Protocol", text= "规约")
        self.mainlist.heading("Configure", text= "配置")
        self.mainlist.pack(fill="both", expand= 1, side="left", anchor="w")
        scrollbarX.configure( command=self.mainlist.xview)
        scrollbarY.configure( command=self.mainlist.yview)
        
        self.confMenu = tk.Menu(self.mainlist)
        self.confMenu.add_command(label = "设置", command = lambda:ui_conf.get_conf_and_set(self, self.UI))
        self.confMenu.add_command(label = "退出", command = self.quit)
        self.confMenu.add_command(label = "显示日志", command = self.on_item_command_show_log )
        self.confMenu.add_command(label = "保存日志", command = self.on_item_command_save_log )
        self.confMenu.add_command(label = "清空日志", command = self.on_item_command_clear_log )
        self.confMenu.add_command(label = "显示菜单", command= self.show_menu)
        
        self.mainlist.bind("<Button-3>", func = self.on_contextmenu)
        self.mainlist.bind("<Double-1>", func = self.on_item_dbclick)
        pass
    
    def on_contextmenu(self, event:tk.Event):
        """上下文菜单"""
        item_name = self.mainlist.identify_row (event.y)
        
        if( item_name): # 右击了其中一行
            item = self.mainlist.item(item_name )
            id = item["text"]
            if(id in self.UI.ADATA):
                data:ui_data.AData = self.UI.ADATA[id]
                m = tk.Menu(self.mainlist)
                if(data.channel.type == "TCPS"):
                    if(data.channel.status != data.channel.STATUS[data.channel.STATUS_RUN]):
                        m.add_command(label = "启动TCPS", command = lambda: self.on_item_command_start(data))    
                    else:
                        m.add_command(label = "关闭TCPS", command = lambda: self.on_item_command_shutdown(item_name))
                elif(data.channel.type == "TCPA"):
                    if(data.channel.status == data.channel.STATUS[data.channel.STATUS_RUN]):
                        m.add_command(label = "关闭TCP客户端", command = lambda: self.on_item_command_shutdown(item_name))
                elif(data.channel.type == "TCPC"):
                    if(data.channel.status != data.channel.STATUS[data.channel.STATUS_RUN]):
                        m.add_command(label = "TCP连接服务器", command = lambda: self.on_item_command_start(data))
                    else:
                        m.add_command(label = "关闭TCP连接", command = lambda: self.on_item_command_shutdown(item_name))
                elif(data.channel.type == "UDP"):
                    if(data.channel.status != data.channel.STATUS[data.channel.STATUS_RUN]):
                        m.add_command(label = "启动UDP", command = lambda: self.on_item_command_start(data))    
                    else:
                        m.add_command(label = "关闭UDP", command = lambda: self.on_item_command_shutdown(item_name))
                elif(data.channel.type == "COM"):
                    if(data.channel.status != data.channel.STATUS[data.channel.STATUS_RUN]):
                        m.add_command(label = "启动串口", command = lambda: self.on_item_command_start(data))    
                    else:
                        m.add_command(label = "关闭串口", command = lambda: self.on_item_command_shutdown(item_name))
                if(data.channel.status != data.channel.STATUS[data.channel.STATUS_RUN]):
                    m.add_command(label = "修改信息", command = lambda: self.on_item_command_info(item_name, False))
                else:
                    m.add_command(label = "查看信息", command = lambda: self.on_item_command_info(item_name, True))
                m.add_command(label = "删除", command = lambda: self.on_item_command_remove(item_name))
                m.add_command(label = "显示日志", command = self.on_item_command_show_log )
                m.add_command(label = "保存日志", command = self.on_item_command_save_log )
                m.add_command(label = "清空日志", command = self.on_item_command_clear_log )
                m.tk_popup(event.x_root , event.y_root)
        else:
            self.confMenu.tk_popup(event.x_root , event.y_root)
        pass
    
    def on_item_dbclick(self, event:tk.Event):
        items_name = self.mainlist.selection()
        if(not items_name):
            return
        id = self.mainlist.item(items_name[0], "text")
        if( id in self.UI.ADATA):
            data:ui_data.AData = self.UI.ADATA[id]
            self.UI.show(data)
        pass
    
    def on_item_command_start(self, data:ui_data.AData):
        """启动通道"""
        if(data.channel.type == "COM"):
            e = data.channel.start( )
        else:
            e = data.channel.start(self.UI.dqsend)
        if(e != 0):
            tkinter.messagebox.showerror(title="启动出错", message=e)
            return
        self.UI.update(data)
        if(data.channel.type in ["UDP", "COM"]):
            self.UI.enable(data)
            self.UI.show(data)
        self.UI.log("操作", "正在连接：" + data.channel.info + " / " + data.protocol.info )

    def on_item_command_shutdown(self, item_name):
        """断开一个连接， 但是不关闭窗口"""
        if(not item_name):
            return
        id = self.mainlist.item(item_name, "text")
        if id in self.UI.ADATA:
            return self.UI.shutdown(self.UI.ADATA[id], True) # 同时关闭连接
        pass
    
    def on_item_command_info(self, item_name, is_unmodifiable):
        if(not item_name):
            return
        id = self.mainlist.item(item_name, "text")
        if id in self.UI.ADATA:
            data:ui_data.AData = self.UI.ADATA[id]
            d = Dialog_info(self, data, is_unmodifiable)
            if(d.applied):
                if(not is_unmodifiable):
                    data.channel.recreatefd( )
                self.UI.update(data)
        pass
    
    def on_item_command_remove(self, item_name:str):
        """"关闭一个连接及窗口"""
        index = self.mainlist.index(item_name)
        for child in self.mainlist.get_children(item_name ):
            id = self.mainlist.item(child, "text")
            if isinstance(id, int):
                self.mainlist.move(child, "", index)
                index = self.mainlist.index(child)
        id = self.mainlist.item(item_name ,"text")
        if(id in self.UI.ADATA):
            self.UI.remove(self.UI.ADATA[id])
        pass
    
    def on_item_command_show_log(self):
        """显示日志窗口"""
        self.UI.smf.show()
    def on_item_command_save_log(self):
        """保存日志"""
        import os
        s = tkinter.filedialog.asksaveasfilename(defaultextension = ".log",
                                                initialfile="msg.log",
                                                initialdir= os.getcwd(),
                                                filetypes=[("log file", ".log"), ("text files",".txt"), ("all files", ".*")])
        if s == '' : return
        f =  open(s, mode="a+" )
        t = self.UI.smf.pLog.get("1.0", "end")
        f.write(t)
        f.close()
    
    def on_item_command_clear_log(self):
        """清空日志"""
        self.UI.smf.pLog.configure(state="normal")
        self.UI.smf.pLog.delete("1.0", "end")
        self.UI.smf.pLog.configure(state="disabled")

    def add(self, data:ui_data.AData)->str:
        """添加一个state"""
        ups = ""
        if hasattr(data.channel,"ups"):
            id = data.channel.ups.channel.id
            for item_name in self.mainlist.get_children():
                row_id = self.mainlist.item(item_name, "text")
                if(row_id == id):
                    ups = item_name
                    break
        row = self.mainlist.insert(ups, "end",\
                    text = data.channel.id, \
                    values= [ \
                        data.channel.type , \
                        data.protocol.type , \
                        data.channel.info + "/" + data.protocol.info ])
        self.mainlist.see( row )
        self.mainlist.selection_set( row )
        return row
        pass
    
    def show(self, data:ui_data.AData):
        """显示并选择这一行"""
        self.mainlist.see( data.protocol.ml )
        self.mainlist.selection_set( data.protocol.ml )
            
    def show_menu(self):
        if(self.confMenu.entryconfigure(self.confMenu.index(5), "label" )[4] == "显示菜单"):
            menubar = tk.Menu(self.UI.root)
            self.UI.root.configure(menu=menubar)
            menu  = tk.Menu(menubar)
            menubar.add_cascade(label="设置",menu=menu)
            menu.add_command(label="设置", command=lambda:ui_conf.get_conf_and_set(self, self.UI))
            menu.add_command(label="退出", command=self.winfo_toplevel().quit)
            
            menu.add_command(label="关于", command=lambda:about.About(self.UI.root))
            self.confMenu.entryconfigure(self.confMenu.index(5), label="关闭菜单")
        else:
            self.confMenu.entryconfigure(self.confMenu.index(5), label="显示菜单")
            emptymenubar = tk.Menu(self.UI.root)
            self.UI.root.configure(menu=emptymenubar)
        pass
        
    def remove(self, data:ui_data.AData):
        """删除一条"""
        id =  data.channel.id
        stack = list(self.mainlist.get_children())
        row = None
        while stack:
            child = stack.pop() # 从尾部弹出一个元素
            children = self.mainlist.get_children(child)
            if(children):
                stack += children
                continue
            if(id == self.mainlist.item(child,"text")):
                self.mainlist.delete(child)
                break
        pass
    
    def get_children(self, row = None)->tuple:
        l = []
        for c in self.mainlist.get_children(row):
            if(isinstance(self.mainlist.item(c,"text"), int)):
                l.append((c, self.mainlist.item(c,"text")))
                r = self.get_children(c)
                if(r):
                    l.extend(r)
        return tuple(l)



class Dialog_info(tkinter.simpledialog.Dialog):
    """通道参数设置"""
    def __init__(self,parent, data:ui_data.AData, is_unmodifiable = True) -> None:
        self.data = data
        self.applied = False
        self.is_unmodifiable =  is_unmodifiable
        super().__init__(parent, title="通道参数：" )
        pass
    def body(self, master) -> None:
        self.pFrame:tk.PanedWindow = tk.PanedWindow(master,  orient="vertical" , showhandle=True,width=550, height=450)
        self.pFrame.pack(fill="both", expand = 1)
        c , h = self.data.channel.GUI(self.pFrame, self.data.channel)
        self.pFrame.add(c )
        self.pFrame.paneconfigure(c, sticky="nwes" )
        p = self.data.protocol.GUI(self.pFrame, self.data.protocol)
        self.pFrame.add(p)
        if(self.is_unmodifiable):
            for w in c.winfo_children():
                w.configure(state="disabled")
            for w in p.winfo_children():
                w.configure(state="disabled")
    def apply(self):
        self.data.channel.apply()
        self.data.protocol.apply()
        self.applied = True
